2026_daft_punk_revolution_909.py

#

SPDX-FileCopyrightText: 2026 Ayman Amara Hammou SPDX-FileCopyrightText: 2026 AlICe laboratory https://alicelab.be

SPDX-License-Identifier: GPL-3.0-or-later

#
import math
import os
import random

import bmesh
import bpy
#

creat rad to degree

def rtd(deg):
    return math.radians(deg)


os.system("cls")
#
def clean():
    bpy.ops.object.select_all(action="SELECT")

    bpy.data.objects["0LIMIT"].select_set(False)
    bpy.data.objects["0LIMBOOL"].select_set(False)

    bpy.ops.object.delete()
    bpy.ops.outliner.orphans_purge()


clean()
#
#

BASE - TR909

#

fonction de la trame TR909

def TR909(_Name, _Pos, _Size):
    bpy.ops.mesh.primitive_cube_add(location=_Pos, scale=_Size)
    bpy.context.object.name = _Name
    bpy.context.object.data.name = _Name + "_Mesh"


TR_Pos = [2, 4, 6, 8]
TR_created = []
#

Barres paralleles à l’axe X

for y in TR_Pos:
    for z in TR_Pos:
        TR = TR909("TR X " + str(y), (5.0, y, z), (9.00 / 2, 0.15, 0.15))
        TR_created.append(TR)
#

Barres paralleles à l’axe Y

for x in TR_Pos:
    for z in TR_Pos:
        TR = TR909("TR Y " + str(x), (x, 5.0, z), (0.15, 9.00 / 2, 0.15))
        TR_created.append(TR)
#

Barres paralleles à l’axe Z

for x in TR_Pos:
    for y in TR_Pos:
        TR = TR909("TR Z " + str(x), (x, y, 5.0), (0.15, 0.15, 9.00 / 2))
        TR_created.append(TR)
#
#

PART 1 - BASS

#

fonction des cubes BASS

def BASS(_Name, _Pos, _Size, _Rot):
    bpy.ops.mesh.primitive_cube_add(location=_Pos, scale=_Size, rotation=_Rot, size=1)
    bpy.context.object.name = _Name
    bpy.context.object.data.name = _Name + "_Mesh"
#

alignement de la surface inferieur des cubes à Z=0

    bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="BOUNDS")
    bpy.context.object.location.z += _Size[2] / 2
#

dimensionement des cubes Bass, et de la vague

dim_X = 0.6
dim_Y = 0.6
WaveNum = random.choice([-3, -2, -1, 1, 2, 3])
WaveStr = random.uniform(0.75, 2.5)
#

double loop pour la creation des boite dans une zone carré

for x in range(17):
    for y in range(17):
        shake_Z = random.uniform(-0.5, 0.5)  # effet de tremblement
#

creation de l’effet damier, permet de generer 1cube sur 2

        if (x + y) % 2 == 1:
            continue
#

creation de l’effet vague pour la hauteur des damier

        normalized_y = y / 16
        angle = normalized_y * math.pi * WaveNum
        dim_Z = 2 + math.sin(angle) * WaveStr
#

generation des cubes Bass

        BASS(
            "Bass " + str(x),
            (0.6 + x * (dim_X - 0.05), 0.6 + y * (dim_Y - 0.05), 0),
            (dim_X, dim_Y, dim_Z + shake_Z),
            (0, 0, 0),
        )
#
#

PART 2 - DRUMS

#

fonction des cylindres BRUMS

def DRUMS(_Name, _Pos, _Radius, _Height):
    bpy.ops.mesh.primitive_cylinder_add(location=_Pos, radius=_Radius, depth=_Height)
    bpy.context.object.name = _Name
    bpy.context.object.data.name = _Name + "_Mesh"
#

alignement de la surface superieur des cylindres à Z=10

    bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="BOUNDS")
    bpy.context.object.location.z = 10 - (_Height / 2)
#

dimensionement des cylindres Brums, et de la vague

dim_X = 0.6
dim_Y = 0.6
WaveNum = random.choice([-3, -2, -1, 1, 2, 3])
WaveStr = random.uniform(0.75, 2.5)
#

double loop pour la creation des cylindres dans une zone carré

for x in range(17):
    for y in range(17):
        shake_Z = random.uniform(-0.5, 0.5)  # effet de tremblement
        radius = random.uniform(0.3, 0.45)
#

creation de l’effet damier, permet de generer 1 ccylindre sur 2

        if (x + y) % 2 == 1:
            continue
#

localisation des cylindres Brums

        pos_x = 0.6 + x * (dim_X - 0.05)
        pos_y = 0.6 + y * (dim_Y - 0.05)
#

creation de l’effet vague pour la hauteur du damier

        normalized_y = y / 16.0
        angle = normalized_y * math.pi * WaveNum
        height = 2 + math.sin(angle) * WaveStr
#

generation des cylindres Brums

        DRUMS("Brums " + str(x), (pos_x, pos_y, 0), radius, (height + shake_Z))
#
#

PART 3 - GUITAR

#

GUITAR

#

fonction des grilles GUITAR

def GRID(_Name, _GridZ):
    bpy.ops.mesh.primitive_grid_add(
        x_subdivisions=10,
        y_subdivisions=10,
        location=(5, 5, _GridZ),
        size=11,
    )
    grid = bpy.context.object
    grid.name = _Name
    grid.data.name = _Name + "_Mesh"
#

dimensionement de la vague sin

WaveNum = random.choice([-3, -2, -1, 1, 2, 3])
WaveStr = random.uniform(0.75, 2.5)
#

fonction de la vague SIN

def GRID_SIN(_Name):
    for vert in bpy.data.objects[_Name].data.vertices:
        normalized_x = (vert.co.x) / 11
#

Calcul de SIN

        angle = normalized_x * math.pi * WaveNum
        z_offset = math.sin(angle) * WaveStr
        vert.co.z = z_offset
#

Mise à jour du mesh

    bpy.data.objects[_Name].data.update()
#

fonction des modifications

def GRID_MODIFY(_Name, _Thickness):
#

adoucicement de la vague

    waves = bpy.data.objects[_Name].modifiers.new(name="waves", type="SUBSURF")
    waves.levels = 4
    waves.boundary_smooth = "PRESERVE_CORNERS"
#

bpy.ops.object.modifier_apply(modifier=”waves”)

#

creation de l’epaisseur

    extrud = bpy.data.objects[_Name].modifiers.new(name="extrud", type="SOLIDIFY")
    extrud.thickness = _Thickness
#

bpy.ops.object.modifier_apply(modifier=”extrud”)

#

Definition de les listes

total_faces = range(100)
faces_to_change = random.sample(total_faces, random.randint(0, 50))
opposite_faces = [i for i in total_faces if i not in faces_to_change]
#

fonction de suppretion des faces

def GRID_DELETE(_Name, _Faces):
    myObj = bpy.data.objects[_Name]
    myObjGeom = myObj.data
    myBmesh = bmesh.new()
    myBmesh.from_mesh(myObjGeom)

    myBmesh.faces.ensure_lookup_table()
    _Faces = [myBmesh.faces[i] for i in _Faces]
    bmesh.ops.delete(myBmesh, geom=_Faces, context="FACES")
    myBmesh.faces.ensure_lookup_table()
    isolated_faces = []

    for f in myBmesh.faces:
        has_neighbor = False
        for e in f.edges:
            if len(e.link_faces) > 1:
                has_neighbor = True
                break
        if not has_neighbor:
            isolated_faces.append(f)

    bmesh.ops.delete(myBmesh, geom=isolated_faces, context="FACES")

    myBmesh.to_mesh(myObjGeom)
    myObj.data.update()
    myBmesh.free()
#

generation de la grille Guitar1

GRID("Guitar 1", 3)
GRID_SIN("Guitar 1")
GRID_MODIFY("Guitar 1", 0.15)
GRID_DELETE("Guitar 1", faces_to_change)
#

generation de la grille Guitar2

GRID("Guitar 2", 7)
GRID_SIN("Guitar 2")
GRID_MODIFY("Guitar 2", 0.15)
GRID_DELETE("Guitar 2", opposite_faces)
#
#

LIMITATION

#
def B_Lim(_Operand_A, _Operand_B):
    bool_lim = _Operand_A.modifiers.new(name="limit", type="BOOLEAN")
    bool_lim.operation = "INTERSECT"
    bool_lim.object = _Operand_B
    bool_lim.solver = "FLOAT"


grid1 = bpy.data.objects["Guitar 1"]
grid2 = bpy.data.objects["Guitar 2"]
lim = bpy.data.objects["0LIMBOOL"]

B_Lim(grid1, lim)
B_Lim(grid2, lim)
#
#
#

PART 4 - HI-HATS

#

fonction des spheres HI-HATS

def HIHATS(_Name, _Pos, _Radius):
    bpy.ops.mesh.primitive_uv_sphere_add(location=_Pos, radius=_Radius)
    bpy.context.object.name = _Name
    bpy.context.object.data.name = _Name + "_Mesh"
#

nombre de spheres

HHnum = random.choice([0, 0, 3, 6, 9])
#

loop de generation des spheres

for h in range(HHnum):
#

dimensionement des spheres

    _HiDim = random.uniform(0.5, 1.5)
#

localisation des spheres

    loc_x = random.uniform(1, 9)
    loc_y = random.uniform(1, 9)
    loc_z = random.uniform(1.5, 8.5)
#

generation des spheres HI-HATS

    HIHATS("Hi-Hats " + str(h), (loc_x, loc_y, loc_z), _HiDim)
#
bpy.ops.object.select_all(action="DESELECT")